package com.example.db.impl;

import com.example.annotations.Autowired;
import com.example.annotations.Service;
import com.example.c3p0.C3P0DataSource;
import com.example.db.DBSql;
import com.example.db.SqlBuilder;
import com.example.db.TableService;
import com.example.db.model.DBModel;
import com.example.db.model.IPage;
import com.example.db.utils.SqlUtils;
import com.example.utils.DataSourceUtils;
import com.example.utils.reflect.ReflectUtils;
import com.google.protobuf.compiler.PluginProtos;


import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Classname TableServiceImpl
 * @Description TODO
 * @Date 2022/5/29 20:37
 * @Created by XiongXiong
 * @Author: X.I.O
 */
@Service
public class TableServiceImpl implements TableService {

        @Autowired
        DBSql dbSql;

      @Override
      public <T> T getOneById(Object id, Class<?> cls) {
          DBModel dbModel=getDBModel(cls);
          String byIdSql=dbModel.getByIdSql(id);
          return this.selectMapObj(byIdSql,dbModel);
      }

//      @Override
//      public <T> T updateOneById(T t, Class<?> cls) {
//        DBModel dbModel = getDBModel(cls);
//        String updateSql=dbModel.updateSql(t);
//        this.updateSql(updateSql);
//        return t;
//      }

    @Override
    public <T> void deleteById(Object id,Class<T> cls) {
        DBModel dbModel = getDBModel(cls);
        String sql=String.format("delete %s where %s ='%s'",dbModel.getTableName(),dbModel.getId(),id);
        updateSql(sql);
    }

//    @Override
//      public <T> T saveOne(T t, Class<?> cls) {
//        DBModel dbModel = getDBModel(cls);
//        String sqlSql=dbModel.getSaveSQL(t);
//        this.insert(sqlSql);
//        String sql=String.format("select  LAST_INSERT_ID() from %s limit 1",dbModel.getTableName());
//        int id=this.selectCount(sql);
//        System.out.println(id);
//        dbModel.setFieldValue(t,id,dbModel.getId());
//        return t;
//      }

    /**
     * insert into table_name ('field_1','field_2') values ('?','?')
     * @param t
     * @param cls
     * @param <T>
     * @return
     */
    @Override
    public <T> T save(T t, Class<?> cls) {
        if(t == null){
            return null;
        }
        DBModel dbModel = getDBModel(cls);
        StringBuilder stringBuilder=new StringBuilder();
        Map<String,String> map =dbModel.getFields();
        String tableName=dbModel.getTableName();
        Field[] fields=dbModel.getFieldList();
        //存放获取到的值
        List<Object> objects=new ArrayList<Object>();
        boolean first=true;
        stringBuilder.append("insert into ").append(tableName).append("(");
        for (Map.Entry<String,String> mapField:map.entrySet()){
            String fieldName=mapField.getKey();
            String tableField=mapField.getValue();
            Field field=ReflectUtils.getFieldByName(fields,fieldName);
            if(field==null){
                continue;
            }
            Object v=ReflectUtils.getFieldValue(field,t);
            if(v==null){
                continue;
            }
            if (first) {
                stringBuilder.append("`").append(tableField).append("`");
                first = false;
            }else {
                stringBuilder.append(",`").append(tableField).append("`");
            }
            objects.add(v);
        }
        if(objects.size()==0){
            return null;
        }
        first=true;
        stringBuilder.append(") values (");
        for (int i = 0; i < objects.size(); i++) {
            if (first) {
                stringBuilder.append("?");
                first = false;
            }else {
                stringBuilder.append(",?");
            }
        }
        stringBuilder.append(")");
        String sql=stringBuilder.toString();
        dbSql.insertOrUpdateObj(sql,objects);
        String sqlId=String.format("select  LAST_INSERT_ID() from %s limit 1",dbModel.getTableName());
        int id=this.selectCount(sqlId);
        dbModel.setFieldValue(t,id,dbModel.getId());
        return t;
    }


    @Override
    public <T> T updateById(T t, Class<?> cls) {
        if(t == null){
            return null;
        }
        DBModel dbModel = getDBModel(cls);
        StringBuilder stringBuilder=new StringBuilder();
        Map<String,String> map =dbModel.getFields();
        String tableName=dbModel.getTableName();
        Field[] fields=dbModel.getFieldList();
        //存放获取到的值
        List<Object> objects=new ArrayList<Object>();
        boolean first=true;
        //UPDATE `start-prd`.`sys_user` SET `email` = '11', `work` = '111' WHERE `id` = 106
        stringBuilder.append(" update ").append(tableName).append(" set ");
        Field idField=null;
        for (Map.Entry<String,String> mapField:map.entrySet()){
            String fieldName=mapField.getKey();
            String tableField=mapField.getValue();
            if(tableField.equals(dbModel.getId())){
                idField=ReflectUtils.getFieldByName(fields,fieldName);
                continue;
            }
            Field field=ReflectUtils.getFieldByName(fields,fieldName);
            if(field==null){
                continue;
            }
            Object v=ReflectUtils.getFieldValue(field,t);
            if(v==null){
                continue;
            }
            if (first) {
                stringBuilder.append("`").append(tableField).append("`= ? ");
                first = false;
            }else {
                stringBuilder.append(",`").append(tableField).append("`= ? ");
            }
            objects.add(v);
        }
        if(objects.size()==0){
            return null;
        }
        if(idField==null){
            System.out.println("主键id不存在");
            return null;
        }
        Object id=ReflectUtils.getFieldValue(idField,t);
        stringBuilder.append("where ").append(dbModel.getId()).append(" = ?");
        objects.add(id);
        String sql=stringBuilder.toString();
        System.out.println(sql);
        dbSql.insertOrUpdateObj(sql,objects);
        return t;
    }


    @Override
      public SqlBuilder getSqlBuilder(Class<?> cls) {
          DBModel dbModel=getDBModel(cls);
          return dbModel.getSqlBuilder();
      }

      @Override
      public <T> T selectOne(SqlBuilder sqlBuilder,Class<?> cls) {
          DBModel dbModel = getDBModel(cls);
          String sql=sqlBuilder.getSql();
        System.out.println(sql);
          return this.selectMapObj(sql,dbModel);
      }

      @Override
      public <T> List<T> selectList(SqlBuilder sqlBuilder,Class< ? > cls) {
        DBModel dbModel = getDBModel(cls);
        String sql=sqlBuilder.getSql();
        return this.selectList(sql,dbModel);
      }

      @Override
      public <T> IPage<T> getPage(SqlBuilder sqlBuilder, Class< ? > cls) {
        DBModel dbModel = getDBModel(cls);
        String page = sqlBuilder.getPage();
        String count=sqlBuilder.getCountSql();
        System.out.println(page);
        System.out.println(count);
        List<T> list=this.selectList(page,dbModel);
        int totalCount=this.selectCount(count);
        return new IPage<>(totalCount,list);
      }

      @Override
      public <T> List<T> getList(Class<?> cls) {
          DBModel dbModel=getDBModel(cls);
          String sql=dbModel.getSelectAllList();
          return this.selectList(sql,dbModel);
      }




      public DBModel getDBModel(Class< ? > cls) {
          return SqlUtils.getDBModelByClass(cls);
      }

      private boolean updateSql(String sql){
          System.out.println(sql);
        Connection connection=null;
        try {
            connection=getConnection();
            sqlExecute(sql,connection);
        }catch (Exception e) {
          e.printStackTrace();
        }finally {
          closeConnection(connection);
        }
        return true;
      }


      private <T> List<T> selectList(String sql,DBModel dbModel){
          System.out.println(sql);
          Connection connection=getConnection();
          ResultSet rs = this.sqlExecuteQuery(sql,connection);
          if(rs==null){
              return new ArrayList<>();
          }
          try {
            List<T> tList=new ArrayList<>();
            int n=0;
            String[] fields = this.getFeildList(rs);
            while (rs.next()){
                T t=(T) dbModel.getObject();
                for(int i = 0; i < fields.length; ++i) {
                  String dbField = fields[i];
                  Object value = rs.getObject(dbField);
                  dbModel.setFieldValueByDBField(t,value,dbField);
                }
                tList.add(t);
            }
            return tList;
          }catch (Exception e) {
              e.printStackTrace();
          }finally {
              closeConnection(connection);
          }
          return null;
      }


      private  <T> T selectMapObj(String sql,DBModel dbModel) {
          System.out.println(sql);
          Connection connection=getConnection();
          ResultSet resultSet = this.sqlExecuteQuery(sql,connection);
          if (resultSet == null) {
              return (T)dbModel.getObject();
          }
          T t=(T)dbModel.getObject();
          String[] fields = this.getFeildList(resultSet);
          try {
            if (resultSet.next()) {
              for(int i = 0; i < fields.length; ++i) {
                  String dbField = fields[i];
                  Object value = resultSet.getObject(dbField);
                  dbModel.setFieldValueByDBField(t,value,dbField);
              }
              return t;
            }
          } catch (Exception var11) {
            var11.printStackTrace();
          }finally {
            closeConnection(connection);
          }
          return null;
      }

      public int selectCount(String sql){
          System.out.println(sql);
          Connection connection = getConnection();
          ResultSet resultSet = this.sqlExecuteQuery(sql,connection);
          try {
            resultSet.next();
            int count = resultSet.getInt(1);
            return count;
          }catch (SQLException e){
            e.printStackTrace();
          }finally {
              closeConnection(connection);
          }
          return 0;
      }

      private boolean insert(String sql){
          System.out.println(sql);
          Connection conn = null;
          PreparedStatement sta=null;
          try {
            conn=getConnection();
            sta=conn.prepareStatement(sql);
            boolean b= sta.execute();
            return b;
          }catch (Exception var12) {
            var12.printStackTrace();
          }finally {
              closeConnection(conn);
          }
          return false;
      }

      private String[] getFeildList(ResultSet rs) {
        try {
          ResultSetMetaData rsmd = rs.getMetaData();
          String[] lie = new String[rsmd.getColumnCount()];
          for(int i = 1; i < rsmd.getColumnCount() + 1; ++i) {
            lie[i - 1] = rsmd.getColumnName(i);
          }
          return lie;
        } catch (Exception var5) {
          var5.printStackTrace();
          return new String[0];
        }
      }

      private Connection getConnection(){
        C3P0DataSource c3P0DataSource= DataSourceUtils.getC3P0DataSource();
        return c3P0DataSource.getConnection();
      }

      private void closeConnection(Connection connection){
        C3P0DataSource c3P0DataSource= DataSourceUtils.getC3P0DataSource();
        c3P0DataSource.clonse(connection);
        connection=null;
      }

    private ResultSet sqlExecuteQuery(String sql,Connection conn) {
      Statement stmt = null;
      ResultSet rs = null;
      try {
        if (conn == null) {
            return null;
        } else {
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            return rs;
        }
      } catch (Exception var5) {
        var5.printStackTrace();
        return rs;
      }
    }

    private boolean sqlExecute(String sql,Connection conn) {
      boolean flag = false;
      Statement stmt = null;
      try {
        stmt = conn.createStatement();
        flag = stmt.execute(sql);
        return flag;
      } catch (Exception var8) {
        var8.printStackTrace();
      }
      return flag;
    }

}
